import { inject, ref, toRef, onBeforeUnmount, provide, watch } from "vue";
import {
  Rule,
  ValidatorError,
  ValidatorErrorResult,
  makeValidators,
} from "../validate";
import { Form, formInjectKey, FormField, fieldInjectKey } from "./types";
import { isEmpty } from "../utils";

export const useField = (name: string, rule?: Rule, initValue?: any) => {
  const form = inject<Form | null>(formInjectKey, null),
    error = ref<ValidatorErrorResult>([]),
    formValues = form?.values;

  // get value by value or form
  const value =
    formValues?.[name] != undefined ? toRef(formValues, name) : ref(initValue);
  //
  const valid = makeValidators(isEmpty(rule) ? form?.getRule(name) : rule),
    setError = (messages: ValidatorErrorResult) => {
      error.value = messages;
    },
    reset = () => {
      error.value = [];
    },
    validate = async (isThrow = true) => {
      try {
        const result = await valid(value.value, formValues);
        error.value = [];
        return result;
      } catch (err) {
        const messages = (err as ValidatorError).messages;
        error.value = messages;
        if (isThrow) throw err;
      }
    };
  // form field
  const field: FormField = {
    name,
    setError,
    reset,
    validate,
  };
  provide<FormField>(fieldInjectKey, field);
  // add field
  form?.addField(field);
  // remove field
  onBeforeUnmount(() => form?.removeField(field));
  // watch value
  watch(value, async () => await validate(false));

  return {
    value,
    error,
    validate,
    setError,
    reset,
  };
};

export default useField;
